package com.blyat.xsoft.kernel.component.security;

import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author syh
 * @date 2019/9/29
 **/
public class InvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

    private final Set<String> ignoreUrl = Sets.newHashSet();
    private FilterInvocationSecurityMetadataSource metadataSource;
    private AuthorityDetailsService authorityDetailsService;

    public InvocationSecurityMetadataSourceService(FilterInvocationSecurityMetadataSource metadataSource) {
        this.metadataSource = metadataSource;
    }

    public void setAuthorityDetailsService(AuthorityDetailsService authorityDetailsService) {
        this.authorityDetailsService = authorityDetailsService;
    }

    // 必须是本平台的用户
    public static final Collection<ConfigAttribute> defaultAttribute
            = Sets.newHashSet(new SecurityConfig(SecurityConstant.SECURITY_AUTH_PREFIX));

    // 存放资源配置对象
    private static Map<RequestMatcher, Collection<ConfigAttribute>> resourceMap = null;

    public Set<String> ignore(String... ignore) {
        ignoreUrl.addAll(Sets.newHashSet(ignore));
        return ignoreUrl;
    }

    /**
     * 每次请求后台都会调用该方法得到这个url所需要的权限
     * 除白名单列表外所有url请求都需要XSOFT权限(所有登录用户都有，匿名用户都没有)才可以访问。即必须是平台用户
     * 这个方法在url请求时才会调用，服务器启动时不会执行这个方法
     * getAttributes这个方法会根据你的请求路径去获取这个路径应该是有哪些权限才可以去访问。
     * todo 该方案并没有解决垂直越权问题。
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        if (resourceMap == null) {
            loadAuthority();
        }

        HttpServletRequest request = ((FilterInvocation) object).getRequest();

        // 忽略权限验证
        for (String white : ignoreUrl) {
            RequestMatcher matcher = new AntPathRequestMatcher(white);
            if (matcher.matches(request)) {
                return null;
            }
        }

        // 获取url所需要的所有权限
        Iterator<RequestMatcher> ite = resourceMap.keySet().iterator();
        while (ite.hasNext()) {
            RequestMatcher matcher = ite.next();
            if (matcher.matches(request)) {
                return resourceMap.get(matcher);
            }
        }

        /*Collection<ConfigAttribute> attrs = metadataSource.getAttributes(object);
        if (attrs != null) {
            return attrs;
        }*/

        return defaultAttribute;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

    // todo 事件驱动：菜单更新时  触发此方法
    private void loadAuthority() {
        List<AuthorityDetails> resourceList = authorityDetailsService.loadAuthorities();
        if (resourceList != null && !resourceList.isEmpty()) {
            resourceMap = new ConcurrentHashMap<>();
            for (AuthorityDetails authority : resourceList) {
                if (!Strings.isNullOrEmpty(authority.getPath())) {
                    String url = authority.getPath();

                    RequestMatcher matcher = new AntPathRequestMatcher(url);
                    Collection<ConfigAttribute> attributes = resourceMap.getOrDefault(matcher, new HashSet<>());

                    resourceMap.put(matcher, attributes);

                    attributes.add(new SecurityConfig(
                            String.format("%s_%s", SecurityConstant.SECURITY_AUTH_PREFIX, authority.getId())
                    ));
                }
            }
        }
    }

}
